package team3.tracking.communication;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketException;
import java.text.Format;
import java.text.SimpleDateFormat;
import java.util.Date;
public class TCPServerHandler {
	  
		boolean statusLog;
		ServerSocket serverTCPSocket;
		final static int TCPServerPortID = 8000;
		private ThreadPool threadPool;
		Main_TCPThread TCPThread;
		static String time;
		static Format currentTime;
		static Date now;
		static boolean allowRun = true;
		FileManagement fileManagement;
		
		public  TCPServerHandler(ThreadPool threadPools ,FileManagement fileManagement)
		{		
			this.fileManagement=fileManagement;
			System.out.println("Started");
			this.threadPool=threadPools;
			this.threadPool.execute(new Main_TCPThread());
			//the Main_TCPThread keeps running in a loop waiting for queries
			//when its socket accepts a connection, it executes a TCP_Thread (via the ThreadPool)
		}
		
		public class TCP_Thread implements Runnable  {	
			Socket clientSocket;
			InputStream inputStream;
			OutputStream outputStream;
			String queryClientID;
			ClientData currentPredictedPosition;
			byte[] byteStream;
			ClientData cData;			
			/* Variables for calculating predicted position*/
			long timeDiff;
			double dx, dy;
			/* Constructor of the Class WorkerThread. */
			public TCP_Thread(Socket clientSocket){
				this.clientSocket = clientSocket;
			}				
			@Override
			public void run() {	
				System.out.println("RUN() of TCP Thread");

				try {

					/* Establish input-output streams*/
					inputStream = clientSocket.getInputStream();
					outputStream = clientSocket.getOutputStream();
					System.out.println("WORKER THREAD: Bidirectional TCP connection to client established");

					/* Receive client ID*/
					byteStream = new byte[2000];
					inputStream.read(byteStream);
					cData=PositionInfo.deserializeData(new String (byteStream));
					//receive the client ID (so we know who is making the query)
					queryClientID =cData.getClientID();					
					System.out.println("WORKER THREAD: Query Received from ClientID = " + queryClientID);			
				
						if(null != queryClientID) {	
							/* Receive last position update of this client from the informationBank*/
							currentPredictedPosition = PositionInfo.getPositionInfo(queryClientID);					
							/* If information for this client was available*/
							if(null != currentPredictedPosition){
							
								Long lastServerTime = currentPredictedPosition.getServerTimeStamp();
								Long timeServerDiference = (new Date().getTime() - lastServerTime)/1000;
								
								ClientData cData = new ClientData();
								cData.setLatitude(currentPredictedPosition.getLatitude()+
										currentPredictedPosition.getPredictionVectorX()*timeServerDiference);
								cData.setLongitude(currentPredictedPosition.getLongitude()+
										currentPredictedPosition.getPredictionVectorY()*timeServerDiference);
								
								System.out.println("new Date.getTime()= "+new Date().getTime());
								System.out.println("currentPredictedPosition.getServerTimeStamp()= "+currentPredictedPosition.getServerTimeStamp());
								System.out.println("Server Time Difference "+timeServerDiference);

								System.out.println("Latitude Before Predection "+currentPredictedPosition.getLatitude());
								System.out.println("Latitude After Calculation" + cData.getLatitude());

								System.out.println("Vector X" +currentPredictedPosition.getPredictionVectorX());
								System.out.println("Vector Y" +currentPredictedPosition.getPredictionVectorY());

								System.out.println("Longtiude Before Predection "+currentPredictedPosition.getLongitude());
								System.out.println("Longitude After Calculation" + cData.getLongitude());

							byteStream =PositionInfo.serializeData(cData);
							//send all this data (serialized) to the client (that made the query)
							outputStream.write(byteStream);	
							/* Closing input and output streams as well as the TCP socket */
							inputStream.close();
							outputStream.close();
							clientSocket.close();
							}
							else
							{
								System.out.println("Client Query Null");
								
							}
	
						}
					
					else{
						allowRun = false;
						
						/* Closing input and output streams as well as the TCP socket */
						inputStream.close();
						outputStream.close();
						clientSocket.close();
						
						System.out.println("WORKER THREAD: All sockets closed. Quitting ...");
					}


				}

				catch(IOException e){
					e.printStackTrace();
				}
				
			
				}
					
				
				
			
			  	
		}
        public class Main_TCPThread implements  Runnable {
    		/* Constructor for TCP Thread*/
    		public Main_TCPThread(){
    		}
    		
    		@Override
    		public void run() {
    			System.out.println("RUN() of Main TCP Thread");
    			/* calculate time stamp for writing to GPX file*/
    			currentTime = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'");
    			now = new Date();
    			time = currentTime.format(now); 			
    			try{
    				// create socket
    				System.out.println("Connecting to the Socket");
    				serverTCPSocket = new ServerSocket();
    				System.out.println(TCPServerPortID);
    				serverTCPSocket.bind(new InetSocketAddress(TCPServerPortID));    				   				
    				while (allowRun) {
    					try{

    						Socket TCPsocket = serverTCPSocket.accept();
    						System.out.println("Connected");
    						//start a TCP_Thread to handle this query
    						threadPool.execute(new TCP_Thread(TCPsocket));
    					}
    					catch(SocketException e){
    						System.out.println("TCP Socket closed forecfully..");
    					}
    					catch(IOException e){
    						e.printStackTrace();
    					}
    					
    					
    				}
    			}
    			catch(IOException e){
    				e.printStackTrace();
    			}   			
    		   		  
    			System.out.println("TCP thread terminating...");
    		}
		}
		
}